package com.edee.bigday.auth;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

/**
 * 核心原理：
 * 1、@EnableRedisHttpSession在org.springframework.session.data.redis.config.annotation.
 * web.http;中通过@Import(RedisHttpSessionConfiguration.class)导入了RedisHttpSessionConfiguration；
 *      1）在.../RedisHttpSessionConfiguration.java中，为容器添加了一个组件RedisIndexedSessionRepository：
 *      @Bean
 * 	    public RedisIndexedSessionRepository sessionRepository() {...
 * 	    雷神视频显示如下：
 * 	    public RedisOperationsSessionRepository sessionRepository() {...
 * 	    Repository一般是标注在数据库持久化层。上述组件表示Session持久层的Redis操作类，相当于Redis操作session的Dao，
 * 	    session的增删改查的Redis封装类。
 * 	    由于RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration，而后者还是一个配置：
*           @Configuration(proxyBeanMethods = false)
 *          public class SpringHttpSessionConfiguration implements ApplicationContextAware {...
 *      2）SpringHttpSessionConfiguration中的SessionRepositoryFilter为session存储过滤器。
 *          public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {...
 *          abstract class OncePerRequestFilter implements Filter {...
 *          package javax.servlet;
 *          public interface Filter {...
 *          说明SessionRepositoryFilter就是http的Filter。
 *          每个请求进来都必须经过Filter。
 *      3）在RedisIndexedSessionRepository中Ctrl+F12可以查看其中封装的方法：
 *          createSession
 *          deleteById
 *          findById
 *          findByIndexNameAndIndexValue
 *          getExpirationsKey
 *          getExpiredKey
 *          getSession
 *          getSessionAttrNameKey
 *          getSessionBoundHashOperations
 *          可见，其中包含增删改查等各种方法。
 *          通过：
 *          public class RedisIndexedSessionRepository
 * 		    implements FindByIndexNameSessionRepository<RedisIndexedSessionRepository.RedisSession>, MessageListener {。。
 * 		    可知，session是在Redis中做CRUD操作的。
 * 		4）在查看SessionRepositoryFilter:
 * 	        只有一个有参构造器：
 * 	        public SessionRepositoryFilter(SessionRepository<S> sessionRepository) {
 * 		        if (sessionRepository == null) {
 * 		        	throw new IllegalArgumentException("sessionRepository cannot be null");
 *              }
 * 		        this.sessionRepository = sessionRepository;
 * 		    }
 * 		    由上可知，SessionRepository就是保存Session的仓库，该仓库向容器中放置的是【RedisIndexedSessionRepository】，
 * 		    即，将session都放在Redis中。
 * 		    4.1）在SessionRepositoryFilter创建的时候，就自动从容器中获取到了SessionRepository，因为容器中添加的是
 * 		        【RedisIndexedSessionRepository】，所以，自动从容器中获取到的也就是【RedisIndexedSessionRepository】。
 * 		    4.2）在SessionRepositoryFilter中，有以下被重新的方法doFilterInternal():
 *              @Override
 * 	            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
 * 	            		throws ServletException, IOException {
 * 	            	request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
 * 	            //上的this.sessionRepository是全系统唯一的session CRUD等操作的操作类。
 * 	            //为当前请求设置attribute，
 *
 * 	                //对原生的请求对象HttpServletRequest request做了包装，包装成为SessionRepositoryRequestWrapper：
 * 	            	SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
 * 	            	//对原生的响应对象HttpServletResponse response做了包装，包装成为SessionRepositoryResponseWrapper：
 * 	            	SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,
 * 	            			response);
 *
 * 	            	try {
 * 	                    //将包装后的对象应用到后面的整个执行链：
 * 	            		filterChain.doFilter(wrappedRequest, wrappedResponse);
 * 	            	}
 *                     		finally {
 * 	            		wrappedRequest.commitSession();
 * 	            	}
 * 	           	}
 * 	           	上面这个重写的方法就是SpringSession的核心。
 * 	            并且，从doFilterInternal进一步追踪得知，OncePerRequestFilter真正重写了Filter的doFilter方法。
 *
 * 	       5）如果在com/edee/bigday/auth/controller/OAuth2Controller.java使用原生的ServletHttpRequest request：
 *         使用request.getSession()可以后去原生的HttpSession，然后才能在HttpSession中保存数据。
 *         正是利用的这个特性：要想获取session，必须使用request.getSession()。
 *         获取session本应从ServletHttpRequest对象中获取，但在SessionRepositoryFilter中，
 *         原生的ServletHttpRequest对象已经被包装成了SessionRepositoryRequestWrapper。
 *         原生的request和response都被重新进行了包装，分别包装为SessionRepositoryRequestWrapper和
 *         SessionRepositoryResponseWrapper。这样包装的作用到底是什么？
 *         作用是：本应使用原生的ServletHttpRequest request.getSession()，原生的类被包装后，
 *         只能使用SessionRepositoryRequestWrapper wrappedRequest.getSession()，
 *         从而就可以使用包装类中的自定义方法和属性了。这样一来，session的获取方法改变了。
 *
 *         6）综上可以得出，SpringSession使用了装饰器模式，将原生的HttpServletRequest进行包装，
 *         之后再使用其自定义的方法在Redis中操作session。
 *
 *
 */
//整合Redis作为session存储：
@EnableRedisHttpSession
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class BigdayAuthServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(BigdayAuthServerApplication.class, args);
    }

}
